/*   This file is a part of the WBTS Project. WBTS is a system for helping 
 *	 researchers, and browser implementers to find security related bugs.
 *   Copyright (C) 2010 Isaac Dawson
 *
 *    WBTS is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

function JsBlade(frame_ele, error_func) {
	this.current_case = '';
	this.blade_uri = '/cases';
	this.case_server = document.location.protocol + "//" + document.location.hostname;
	// very weird opera annoyance...
	if (document.location.port > 0) {
		this.case_server += ":" + document.location.port;
	}
	this.case_idx = 0;
	this.client_id = fake_guid();
	this.cases = new Array();
	this.frame_element = frame_ele;
	this.xmlrpc_query =  "<?xml version='1.0'?>\n"
	this.xmlrpc_query += "<methodCall>\n<methodName>{0}</methodName>\n<params>\n";
	this.xmlrpc_arg = "<param>\n<value><{0}>{1}</{0}></value>\n</param>\n";
	this.xmlrpc_end_query = "</params>\n</methodCall>\n";
	this.err_out = error_func;
}

JsBlade.prototype.loadUrlsFromWbts = function(test_type) {
	this.getCasesOfType(test_type);
} 
JsBlade.prototype.getTestTypes = function() {
	var test_types = new Array(); 
	var test_type_request = this.xmlrpc_query.replace("{0}", "getTestTypes");
	test_type_request += this.xmlrpc_end_query;
	var xhr = get_xhr();
	xhr.open('POST', this.blade_uri, false);
	xhr.send(test_type_request);
	if (xhr.readyState == 4) {
		try {
			var types = xhr.responseXML.getElementsByTagName("string");
			for (var i = 0; i < types.length; ++i) {
				test_types[i] = types[i].childNodes[0].nodeValue;
			}
			test_types[test_types.length] = "all"; // add all by default.
		} catch(e) {
			this.err_out("[*] Error getting test types:" + e);
		}
	}
	return test_types;
}
JsBlade.prototype.getCasesOfType = function(test_type) {
	var type_arg = this.xmlrpc_arg.replace(/\{0\}/g, "string");
	type_arg = type_arg.replace("{1}", test_type);
	var method = this.xmlrpc_query.replace("{0}", "getTestsOfType");
	var get_cases_request = method + type_arg + this.xmlrpc_end_query;
	var xhr = get_xhr();
	xhr.open('POST', this.blade_uri, false);
	xhr.send(get_cases_request);
	if (xhr.readyState == 4) {
		try {
			var cases = xhr.responseXML.getElementsByTagName("string");
			for (var i = 0; i < cases.length; ++i) {
				this.cases[i] = cases[i].childNodes[0].nodeValue;
			}
			this.err_out("[*] We have a total case count of: " + this.cases.length);
		} catch(e) {
			this.err_out("[*] Error getting case's for testing type: " + test_type + ": " + e);
		}
	}
}

JsBlade.prototype.runSingleTest = function(test_case) {
	this.case_idx = 0;
	this.cases.length = 0;
	this.updateFrameSrc(test_case);
	return;
}

JsBlade.prototype.startTests = function() {
	this.nextCase();
}

JsBlade.prototype.nextCase = function() {
	if (this.case_idx == this.cases.length) {
		this.finish();
	} else {
		this.updateFrameSrc(this.cases[this.case_idx]);
	}
	this.case_idx++;
}

JsBlade.prototype.updateFrameSrc = function(case_uri) {
	// do update
	var jsblade = this;
	case_uri += '?client_id=' + jsblade.client_id; // append our client_id
	var src_url = jsblade.case_server + case_uri; // create the full url.
	//this.frame_element.src = src_url; // and update the frame/iframe src to our next case.
	var start_len = case_uri.lastIndexOf('/')+1;
	var end_len = case_uri.indexOf('?');
	var case_id = case_uri.substr(start_len, end_len-start_len);
	try {
		jsblade.frame_element.setAttribute('src', src_url);
	} catch(e) {
		jsblade.frame_element.src = src_url;
	}
	jsblade.err_out("[*] Updated src attribute to: " + src_url);
	case_id = case_id.replace("-handler.","."); // remove the handler from out of origin test cases.
	
	function check_for_results(jsblade, case_uri, iter) {
		jsblade.err_out('check result for case_uri: ' + case_uri);
		var result = jsblade.parseProgressResults(jsblade, case_uri, jsblade.client_id);
		if (result == "1") { // 1 means we have a result.
			jsblade.err_out("[*] Test [" + jsblade.case_idx + "]: " + case_uri + " FINISHED...going to next case");
			jsblade.nextCase();
			return;
		}
		switch (iter) {
			case 0: 
				// wait 2 sec
				jsblade.err_out("[*] Waiting 2 seconds...");
				setTimeout( (function() { check_for_results(jsblade, case_id, 1) }), 2000);
				break;
			case 1:
				// ugh.. wait more 4 sec
				jsblade.err_out("[*] Waiting 4 seconds...");
				setTimeout( (function() { check_for_results(jsblade, case_id, 2) }), 4000);
				break;
			case 2:
				// ok one more... 4 sec
				jsblade.err_out("[*] Waiting 4 seconds...");
				setTimeout( (function() { check_for_results(jsblade, case_id, 3) }), 4000);
				break;
			default:
				// give up and move on!
				jsblade.failTest(jsblade, case_uri);
				jsblade.err_out("[*] Unable to get the results, moving on to next case.");
				jsblade.nextCase();		
				break;
		}
	}
	jsblade.err_out("[*] Waiting 1 second...");
	setTimeout((function() { check_for_results(jsblade, case_id, 0) }), 1000);	
}

JsBlade.prototype.finish = function() {
	this.err_out("[*] WE'RE DONE!");
}

JsBlade.prototype.setArgs = function (jsblade, case_uri) {
	var case_arg = jsblade.xmlrpc_arg.replace(/\{0\}/g, "string");
	case_arg = case_arg.replace("{1}", case_uri);
	var client_id_arg = jsblade.xmlrpc_arg.replace(/\{0\}/g, "string");
	client_id_arg = client_id_arg.replace("{1}", jsblade.client_id);
	return (client_id_arg + case_arg);
}

JsBlade.prototype.parseProgressResults = function (jsblade, case_uri, client_id) {
	var method = jsblade.xmlrpc_query.replace("{0}", "getTestProgress");
	var args = jsblade.setArgs(jsblade, case_uri)
	var get_cases_request = method + args + jsblade.xmlrpc_end_query;
	
	var xhr = get_xhr();
	var result = 0; // 1 is result exists 0 is result does not
	xhr.open('POST', jsblade.blade_uri, false);
	xhr.send(get_cases_request);
	if (xhr.readyState == 4) {
		try {
			result = xhr.responseXML.getElementsByTagName("int")[0].firstChild.nodeValue;
		} catch(e) {
			//alert(e);
			jsblade.err_out("[*] Error getting response for case_id[" + case_uri + "] and client_id[" + client_id + "]: " + e);
		}
	}
	return result;
}

JsBlade.prototype.failTest = function (jsblade, case_uri) {
	var method = jsblade.xmlrpc_query.replace("{0}", "failTest");
	var args = jsblade.setArgs(jsblade, case_uri)
	var fail_test_request = method + args + jsblade.xmlrpc_end_query;
	var xhr = get_xhr();
	xhr.open('POST', this.blade_uri, false);
	xhr.send(fail_test_request);
	if (xhr.readyState == 4) {
		try {
			result = xhr.responseXML.getElementsByTagName("int")[0].firstChild.nodeValue;
			jsblade.err_out("[*] failTest: Forced test failure");
		} catch(e) {
			//alert(e);
			jsblade.err_out("[*] Error getting response for case_id[" + case_uri + "] and client_id[" + client_id + "]: " + e);
		}
	}
	return result;
}